#include "gagent_def.h"
#include "gagent_cloud.h"

#define     GAGENT_CLOUD_UDP_PORT           12414
#define     GAGENT_CLOUD_TCP_PORT           12416
#define     GAGENT_CLOUD_BROADCAST          2415


extern lan_st *lan;

rt_uint8_t check_udp_recv_data(lan_st *lan)
{
    char *index;

    index = lan->recv_buf;

    //00 00 00 03 03 00 00 03
    //head
    if(memcmp(index, "\x00\x00\x00\x03", 4) != 0)
        return 0;

    index += 4;

    //len
    if(memcmp(index, "\x03", 1) != 0)
        return 0;

    index ++;

    //flag
    if(memcmp(index, "\x00", 1) != 0)
        return 0;

    index ++;

    //cmd
    if(memcmp(index, "\x00\x03", 2) != 0)
        return 0;

   return 1;
}


static int lan_do_udp(lan_st *lan, rt_uint8_t send_type)
{
    char *index;
    rt_uint32_t pack_len, i;
    
    memset(lan->send_buf, 0, sizeof(lan->send_buf));

    index = lan->send_buf;

    //head
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x03;

    //len
    *index ++ = 0x0a;

    //flag
    *index ++ = 0x00;

    //cmd
    *index ++ = 0x00;
    if(send_type == UDP_SEND_TYPE_DISCOVER)
        *index ++ = 0x04;
    else if(send_type == UDP_SEND_TYPE_BOARDCAST)
        *index ++ = 0x05;

    //did_len
    *index ++ = 0x00;
    *index ++ = strlen(lan->con->did);
    //did
    memcpy(index, lan->con->did, 0x16);
    index += strlen(lan->con->did);
    
    //mac_len
    *index ++ = 0x00;
    *index ++ = 0x0c;
    //mac
    memcpy(index, lan->con->mac, 0x0c);
    index += 0x0c;

    //hard_version_len
    *index ++ = 0x00;
    *index ++ = strlen(lan->con->hard_version);
    //hard_version
    memcpy(index, lan->con->soft_version, strlen(lan->con->hard_version));
    index += strlen(lan->con->hard_version);

    //pk_len
    *index ++ = 0x00;
    *index ++ = strlen(lan->con->pk);
    //pk
    memcpy(index, lan->con->pk, strlen(lan->con->pk));
    index += strlen(lan->con->pk);

    index += 8;

    pack_len = index - lan->send_buf;

#if 0
    rt_kprintf("do_udp len:%d ", pack_len);
    for(i = 0; i < pack_len; i ++)
    {
        rt_kprintf("%02x ", lan->send_buf[i]);
    }
    rt_kprintf("\r\n");
#endif
    return pack_len;
}

void gagent_cloud_udp_thread(void *parameter)
{
    lan_st *lan = (lan_st *)parameter;
    struct sockaddr_in server, client;
    struct sockaddr_in broadcast_to;
    int addr_len = sizeof(struct sockaddr_in);
    int rc = 0;
    int opt = 1;
    //
    fd_set readfds;
    int maxfds;
    struct timeval timeout;


    RT_ASSERT(lan != RT_NULL);
    
    while(1)
    {
        lan->udp_server = lwip_socket(AF_INET, SOCK_DGRAM, 0);
        if(lan->udp_server < 0)
        {
            rt_kprintf("udp socket create failed!\n");
            rt_thread_sleep(rt_tick_from_millisecond(3000));
            continue;
        }

        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(GAGENT_CLOUD_UDP_PORT);
        server.sin_addr.s_addr = INADDR_ANY;

        opt = 1;
        lwip_setsockopt(lan->udp_server, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));

        opt = 1;
        lwip_setsockopt(lan->udp_server, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        memset(&broadcast_to, 0, sizeof(broadcast_to));
        broadcast_to.sin_family = AF_INET;
        broadcast_to.sin_port = htons(GAGENT_CLOUD_BROADCAST);
        broadcast_to.sin_addr.s_addr = inet_addr("255.255.255.255");
                
        while(1)
        {
            FD_ZERO(&readfds);
            FD_SET(lan->udp_server, &readfds);

            maxfds = lan->udp_server;
            timeout.tv_sec = 5;
            timeout.tv_usec = 0;

            rc = lwip_select(maxfds + 1, &readfds, 0, 0, &timeout);
            if(rc < 0)
            {
                rt_kprintf("udp socket select failed! errno:%d\n", errno);
                break;
            }
            else if(rc == 0)
            {
//                rt_kprintf("udp socket select timeout!\n");
                lan->send_len = lan_do_udp(lan, UDP_SEND_TYPE_BOARDCAST);
                rc = lwip_sendto(lan->udp_server, lan->send_buf, lan->send_len, 0, 
                                    (struct sockaddr *)&broadcast_to, (socklen_t)sizeof(broadcast_to));
                if(rc <= 0)
                {
                    rt_kprintf("udp socket broadcast failed! errno:%d\n", errno);
                }
                break;
            }


            if(FD_ISSET(lan->udp_server, &readfds))
            {
                lan->recv_len = lwip_recvfrom(lan->udp_server, lan->recv_buf, sizeof(lan->recv_buf), 0, 
                                                (struct sockaddr *)&client, (socklen_t *)&addr_len);

                if(lan->recv_len <= 0)
                {
                    rt_kprintf("udp socket recvfrom failed! errno:%d\n", errno);
                    continue;
                }
                if(check_udp_recv_data(lan))
                {
                    lan->send_len = lan_do_udp(lan, UDP_SEND_TYPE_DISCOVER);
                    rc = lwip_sendto(lan->udp_server, lan->send_buf, lan->send_len, 0, 
                                        (struct sockaddr *)&client, (socklen_t)sizeof(client));
                    if(rc <= 0)
                    {
                        rt_kprintf("udp socket sendto failed! errno:%d\n", errno);
                        break;
                    }
                }
            }
        }
        
        lwip_close(lan->udp_server);
    }
}

static int lan_get_passcode(lan_st *lan)
{
    char *index, *len_index;

    index = lan->send_buf;

    memset(index, 0, sizeof(lan->send_buf));

    //head
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x03;

    //len
    len_index = index;
    index ++;

    //flag
    *index ++ = 0x00;
    
    //cmd
    *index ++ = 0x00;
    *index ++ = 0x07;

    //passcode_len
    *index ++ = 0x00;
    *index ++ = strlen(lan->con->passcode);

    memcpy(index, lan->con->passcode, strlen(lan->con->passcode));
    index += strlen(lan->con->passcode);

    //
    lan->send_len = (index - lan->send_buf);
    *len_index = lan->send_len - HEAD_LEN;
    
    return RT_EOK;

}

static int lan_get_device_info(lan_st *lan)
{
    char *index, *len_index;

    index = lan->send_buf;

    memset(index, 0, sizeof(lan->send_buf));

    //head
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x03;

    //len
    len_index = index;
    index ++;

    //flag
    *index ++ = 0x00;
    
    //cmd
    *index ++ = 0x00;
    *index ++ = 0x14;

    //hard_version
    memcpy(index, lan->con->hard_version, strlen(lan->con->hard_version));
    index += 8;

    //soft_version
    memcpy(index, lan->con->soft_version, strlen(lan->con->soft_version));
    index += 8;

    //mcu_hard
    index += 8;

    //mcu_soft_version
    index += 8;

	//p0 version
	index += 8;

	//remain1
	index += 8;

    //remain2_len
	*index ++ = 0x00;
	*index ++ = 0x00;

	//pk_len
    *index ++ = 0x00;
    *index ++ = strlen(lan->con->pk);

    //pk
    memcpy(index, lan->con->pk, strlen(lan->con->pk));
    index += strlen(lan->con->pk);
	
    lan->send_len = (index - lan->send_buf);
    *len_index = lan->send_len - HEAD_LEN;
    
    return RT_EOK;
}

static int lan_login_device(lan_st *lan, char *packet)
{
    char *index, *len_index;
    char passcode_recv[32];
    uint16_t passcode_len;

    memcpy(&passcode_len, packet + HEAD_LEN + 1 + 2, 2);
    passcode_len = ntohs(passcode_len);

    memset(passcode_recv, 0x00, sizeof(passcode_recv));
    memcpy(passcode_recv, packet + HEAD_LEN + 1 + 2 + 2, passcode_len);
        
    index = lan->send_buf;

    memset(index, 0, sizeof(lan->send_buf));

    //head
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x03;

    //len
    len_index = index;
    index ++;

    //flag
    *index ++ = 0x00;

    //cmd
    *index ++ = 0x00;
    *index ++ = 0x09;

    //result
    if(strncmp(lan->con->passcode, passcode_recv, passcode_len) == 0)
        *index ++ = 0x00;
    else
        *index ++ = 0x01;

    lan->send_len = index - lan->send_buf;
    *len_index = lan->send_len - HEAD_LEN;

    return RT_EOK;
}


static int lan_trans_data(lan_st *lan, char *packet)
{
    uint16_t len, cmd;
    char *index, *index_len, *kv;
    uint16_t kv_len;
    uint8_t length_len, action;

    index = packet;
	len = gagent_parse_rem_len((const uint8_t *)index + 3);
	length_len = gagent_num_rem_len_bytes((const uint8_t*)index + 3);

    index += (HEAD_LEN + length_len);
    cmd = *index ++ * 0x100 + *index ++;
    
	// 00 00 00 03 06 00 00 90 01 01 01 
	// 00 00 00 03 0A 00 00 93 00 00 00 00 01 01 01 
    if(cmd == 0x90)
    {
        action = *index ++;
        kv = *index ++;
        kv_len = len - 4;
        
    }
    else if(cmd == 0x93)
    {
        memcpy(&lan->sn, index, 4);
        index += 4;
        rt_kprintf("lan_sn:%d\n", lan->sn);
        
        action = *index ++;
        kv = index;
        kv_len = len - 8;
    }

    lan->send_len = 0;
    rt_kprintf("atcion:%d, kv_len:%d\n", action, kv_len);
    
    gagent_recv_packet(CMD_FROM_LAN, action, kv, kv_len);
    return RT_EOK;
}

static int lan_heart_beat(lan_st *lan)
{
    char *index, *len_index;

    index = lan->send_buf;

    memset(index, 0, sizeof(lan->send_buf));

    //head
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x00;
    *index ++ = 0x03;

    //len
    len_index = index;
    index ++;

    //flag
    *index ++ = 0x00;

    //cmd
    *index ++ = 0x00;
    *index ++ = 0x16;

    lan->send_len = index - lan->send_buf;
    *len_index = lan->send_len - HEAD_LEN;

    return RT_EOK;
}

static int lan_do_tcp(lan_st *lan)
{
    char *one_packet;
    int one_packet_len;
    short cmd;
    int rc = RT_EOK;

    one_packet = lan->recv_buf;
    while(gagent_get_one_packet(one_packet, &one_packet_len, lan->recv_len) == 0)
    {   
        rc = RT_EOK;

        memcpy(&cmd, one_packet + 6, 2);
        cmd = ntohs(cmd);
        
        switch(cmd)
        {
            case 0x06:
                rc = lan_get_passcode(lan);
            break;

            case 0x08:
                rc = lan_login_device(lan, one_packet);
            break;

            case 0x90:
            case 0x93:
                rc = lan_trans_data(lan, one_packet);
            break;

            case 0x13:
                rc = lan_get_device_info(lan);
            break;

            case 0x15:
                rc = lan_heart_beat(lan);
            break;

            default:
            break;
        }

        one_packet += one_packet_len;
        lan->recv_len -= one_packet_len;
        
        if(rc != 0)
            return rc;
    }

    return rc;
}


void gagent_cloud_tcp_thread(void *parameter)
{
    lan_st *lan = (lan_st *)parameter;
    int tcp_sock;
    struct sockaddr_in tcp_sock_addr;
    int rc = RT_EOK;
    int opt;

    RT_ASSERT(lan != RT_NULL);

    while(1)
    {
        tcp_sock = lwip_socket(AF_INET, SOCK_STREAM, 0);
        if(tcp_sock < 0)
        {
            rt_kprintf("tcp socket create failed!\n");
            rt_thread_sleep(rt_tick_from_millisecond(3000));
            continue;
        }

        tcp_sock_addr.sin_family = AF_INET;
        tcp_sock_addr.sin_port = htons(GAGENT_CLOUD_TCP_PORT);
        tcp_sock_addr.sin_addr.s_addr = INADDR_ANY;
        memset(&tcp_sock_addr.sin_zero, 0, sizeof(tcp_sock_addr.sin_zero));
            
        opt = 1;
        setsockopt(tcp_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        rc = lwip_bind(tcp_sock, (struct sockaddr *)&tcp_sock_addr, (socklen_t)sizeof(tcp_sock_addr));
        if(rc < 0)
        {
            rt_kprintf("tcp socket bind failed! errno:%d\n", errno);
            lwip_close(tcp_sock);
            rt_thread_sleep(rt_tick_from_millisecond(3000));
            continue;
        }

        rc = lwip_listen(tcp_sock, MAX_CLIENT);
        if(rc < 0)
        {
            rt_kprintf("tcp socket listen failed! errno:%d\n", errno);
            lwip_close(tcp_sock);
            rt_thread_sleep(rt_tick_from_millisecond(3000));
            continue;
        }
        
        fd_set readfds;
        int maxfd;
        uint8_t id;
        struct timeval timeout;
        int client_sock;
        struct sockaddr_in client_addr;
        int addr_len = sizeof(client_addr);
        
        for(id = 0; id < MAX_CLIENT; id ++) {
            lan->client_fd[id] = -1;
        } 
        
        while(1)
        {
            FD_ZERO(&readfds);
            FD_SET(tcp_sock, &readfds);
            maxfd = tcp_sock;
            
            for(id = 0; id < MAX_CLIENT; id ++) {
                if(lan->client_fd[id] == -1)
                    continue;

                FD_SET(lan->client_fd[id], &readfds);
                if(lan->client_fd[id] > maxfd)
                    maxfd = lan->client_fd[id];
            }

            timeout.tv_sec = 30;
            timeout.tv_usec = 0;

            rc = lwip_select(maxfd + 1, &readfds, 0, 0, &timeout);
            if(rc < 0)
            {
               rt_kprintf("tcp socket select failed!\n");
                break;
            }
            else if(rc == 0)
            {
                rt_kprintf("tcp socket select timeout!\n");
                continue;
            }
            
            if(FD_ISSET(tcp_sock, &readfds))
            {
                addr_len= sizeof(struct sockaddr);
                client_sock = lwip_accept(tcp_sock, (struct sockaddr *)&client_addr, (socklen_t *)&addr_len);
                if(client_sock < 0)
                {
                    rt_kprintf("tcp socket accept failed! errno:%d\n", errno);
                    break;
                }
                rt_kprintf("client_sock:%d\n", client_sock);

                for(id = 0; id < MAX_CLIENT; id ++) {
                    if(lan->client_fd[id] == -1)
                        break;
                }
                
                if(id >= MAX_CLIENT)
                {
                    rt_kprintf("max client!\n");
                    lwip_close(client_sock);
                    continue;
                }

                lan->client_fd[id] = client_sock;
                rt_kprintf("new client: %d %s port:%d\n", id, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

                //set non_block
//                int flags = fcntl(client_sock, F_GETFL, 0);
//                fcntl(client_sock, F_SETFL, flags | O_NONBLOCK);
            }

            for(id = 0; id < MAX_CLIENT; id ++)
            {
                if(lan->client_fd[id] == -1)
                    continue;
                    
                if(FD_ISSET(lan->client_fd[id], &readfds))
                {
                    memset(lan->recv_buf, 0, sizeof(lan->recv_buf));
                    lan->recv_len = lwip_recv(lan->client_fd[id], lan->recv_buf, sizeof(lan->recv_buf), 0);
                    if(lan->recv_len <= 0)
                    {
                        rt_kprintf("lan:%d lan->recv_len:%d errno:%d\n", id, lan->recv_len, errno);
                        lwip_close(lan->client_fd[id]);
                        lan->client_fd[id] = -1;
                        continue;
                    }

#if 0
                    {
                        rt_kprintf("lan %d recv len:%d\n", id, lan->recv_len);
                        int t;
                        for(t = 0; t < lan->recv_len; t ++)
                        {
                            rt_kprintf("%02x ", lan->recv_buf[t]);
                        }
                        rt_kprintf("\r\n");
                    }
#endif
                    rc = lan_do_tcp(lan);
                    if(rc == RT_EOK && lan->send_len > 0)
                    {
                        rt_kprintf("client:%d len:%d\n", id, lan->send_len);
                        lan->send_len = lwip_send(lan->client_fd[id], lan->send_buf, lan->send_len, 0);
                        if(lan->send_len <= 0)
                        {
                            rt_kprintf("tcp client [%d] send failed! errno:%d\n", id, errno);
                            lwip_close(lan->client_fd[id]);
                            lan->client_fd[id] = -1;
                            continue;
                        }
                    }
                }
            }
        }

        for(id = 0; id < MAX_CLIENT; id ++)
        {
            if(lan->client_fd[id] != -1)
            {
                lwip_close(lan->client_fd[id]);
                lan->client_fd[id] = -1;
            }
        }
        lwip_close(tcp_sock);
    }
}

int gagent_lan_init(lan_st *lan)
{
    RT_ASSERT(lan != RT_NULL);
    
    rt_thread_t udp_thread;
    rt_thread_t tcp_thread;

    rt_timer_t udp_timer;

    lan->udp_server = -1;
    lan->tcp_server = -1;
        
    udp_thread = rt_thread_create("gagent_udp", gagent_cloud_udp_thread, lan, 2048, 15, 20);
    if(udp_thread)
        rt_thread_startup(udp_thread);

    tcp_thread = rt_thread_create("gagent_tcp", gagent_cloud_tcp_thread, lan, 2048, 15, 20);
    if(tcp_thread)
        rt_thread_startup(tcp_thread);

    return RT_EOK;
}

